﻿/*
-----------------------------------------------------------------------------------------------------------------------------
Filename: Native.cs
Description: Manipulates desktops and processes.
             NOTE: Depends on WinAPI.cs in order to run!

Created: Jul 11, 2010
Updated: Apr 06, 2011

 * Desktop/process functions are heavily modified versions of code taken from
   Ivica's VirtualDesktop program. Original code Copyright (C) 2010 Ivica Gjorgjievsk.
   All rights reserved. Website: http://www.igprogram.webs.com. *

Copyright (C) 2009-2011 Eyal Kalderon and contibutors.
-----------------------------------------------------------------------------------------------------------------------------
*/

// Make all necessary declarations.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

// Import the API wrapper.
using WinAPI;

// Add this file to the namespace Native.
namespace Native
{
    /// <summary>
    /// Functions which interact with the API.
    /// </summary>
    public class Functions
    {
        // Begin desktop manipulation class.
        public class DesktopManipulation
        {
            #region ShowDesktopIcons
            /// <summary>
            /// Show/hide the Windows Explorer desktop icons.
            /// </summary>
            /// <param show"show">Input "true" to show the icons, "false" to hide them.</param>
            /// <returns>True if successful, otherwise false.</returns>

            // Make some declarations imported from "user32.dll".
            [DllImport("user32.dll")]
            static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
            [DllImport("user32.dll", SetLastError = true)]
            static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

            public static void ShowDesktopIcons(bool show)
            {
                // Define the Program Manager, which creates the desktop icons.
                IntPtr hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Progman", null);

                // If the user input "true"...
                if (show)
                {
                    // ... then show the icons.
                    ShowWindow(hWnd, 5);
                }
                else
                {
                    // Otherwise, hide the icons.
                    ShowWindow(hWnd, 0);
                }
            }
            #endregion
        }

        // Begin process manipulation class.
        public class Processes
        {
            #region CreateProcess
            /// <summary>
            /// Create a new process on the specified desktop.
            /// </summary>
            /// <param name="name">Name of desktop that process should be placed on.</param>
            /// <param name="path">Executable name or full path.</param>
            /// <param name="args">Executable arguments separated by space and slash (Example: /arg1 /arg2).</param>
            /// <returns>True if successful, otherwise false.</returns>
            public static bool CreateProcess(string name, string path, string args)
            {
                try
                {
                    // Process the CreateProcess parameters.
                    WinAPI.Functions.PROCESS_INFORMATION processInformation = new WinAPI.Functions.PROCESS_INFORMATION();
                    WinAPI.Functions.STARTUPINFO startupInfo = new WinAPI.Functions.STARTUPINFO();
                    startupInfo.cb = Marshal.SizeOf(startupInfo);

                    // Set the process' to start on the desktop specified.
                    startupInfo.lpDesktop = name;

                    // Read process' arguments (if any) and then create and
                    // start the process.
                    string path2 = string.Format("\"{0}\" {1}", path, args); // Format arguments
                    bool result = WinAPI.Functions.CreateProcess(path, path2, IntPtr.Zero, IntPtr.Zero, true,
                            WinAPI.Functions.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInformation);

                    // Return the result.
                    return result;
                }
                catch (Exception ex)
                {
                    // If an error occurs, output the problem to the Debug log and
                    // exit the CreateProcess function.
                    Debug.WriteLine(ex.ToString());
                    return false;
                }
            }
            #endregion
        }

        // Begin the spaces class.
        public class VirtualDesktops
        {
            #region CloseDesktop
            /// <summary>
            /// Close an existing desktop.
            /// </summary>
            /// <param name="name">Desktop Name</param>
            /// <returns>True if successful, otherwise false.</returns>
            public static bool CloseDesktop(string name)
            {
                IntPtr handle = OpenDesktop(name);
                if (handle == IntPtr.Zero) { return false; }

                SwitchDesktop(name);
                Process[] processes = Process.GetProcesses();
                foreach (Process p in processes)
                {
                    if (p.ProcessName == "uShellDesktop.exe" || p.ProcessName == "explorer.exe")
                    {
                        p.Kill();
                    }
                }

                return WinAPI.Functions.CloseDesktop(handle);
            }
            #endregion

            #region CreateDesktop
            /// <summary>
            /// Create a new desktop (if desktop exists, return the desktop's handle).
            /// </summary>
            /// <param name="name">Desktop Name</param>
            /// <returns>Desktop Handle</returns>
            public static IntPtr CreateDesktop(string name)
            {
                return WinAPI.Functions.CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, WinAPI.Functions.rights, IntPtr.Zero);
            }
            #endregion

            #region DesktopExists
            /// <summary>
            /// Check if desktop exists 
            /// </summary>
            /// <param name="name">Desktop name</param>
            /// <returns>True if successful, otherwise false.</returns>
            public static bool DesktopExists(string name)
            {
                IntPtr handle = OpenDesktop(name);
                if (handle == IntPtr.Zero) { return false; }
                return true;
            }
            #endregion

            #region GetDesktopName
            /// <summary>
            /// Get a desktop's name from its handle.
            /// </summary>
            /// <param name="handle">Desktop Handle</param>
            /// <returns>Desktop Name</returns>
            public static string GetDesktopName(IntPtr handle)
            {
                // Prevent errors by checking if 'handle' is empty.
                // If 'handle' is indeed null, return an empty string.
                if (handle == IntPtr.Zero) { return string.Empty; }

                // Get the length of the name, so space can be allocated for it.
                int length = 0;
                WinAPI.Functions.GetUserObjectInformation(handle, 2, IntPtr.Zero, 0, ref length);

                // Get the name of the desktop.
                IntPtr pointer = Marshal.AllocHGlobal(length);
                bool result = WinAPI.Functions.GetUserObjectInformation(handle, 2, pointer, length, ref length);
                string name = Marshal.PtrToStringAnsi(pointer);
                Marshal.FreeHGlobal(pointer);

                // If there are any errors at all, even after checking for a null
                // string, return an empty sting. 
                if (!result) { return string.Empty; }

                // Finally, since all went well, output the name of the desktop.
                return name;
            }
            #endregion

            #region OpenDesktop
            /// <summary>
            /// Open an existing desktop.
            /// </summary>
            /// <param name="name">Desktop Name</param>
            /// <returns>Desktop Handle, IntPtr.Zero if desktop does not exists</returns>
            public static IntPtr OpenDesktop(string name)
            {
                return WinAPI.Functions.OpenDesktop(name, 0, true, WinAPI.Functions.rights);
            }
            #endregion

            #region OpenInputDesktop
            /// <summary>
            /// Open the input (actual) desktop.
            /// </summary>
            /// <returns>Desktop Handle</returns>
            public static IntPtr OpenInputDesktop()
            {
                return WinAPI.Functions.OpenInputDesktop(0, true, WinAPI.Functions.rights);
            }
            #endregion

            #region SwitchDesktop
            /// <summary>
            /// Switch to the desktop of the specified name.
            /// </summary>
            /// <param name="name">Desktop Name</param>
            /// <returns>True if successful, otherwise false.</returns>
            public static bool SwitchDesktop(string name)
            {
                IntPtr handle = OpenDesktop(name);
                if (handle == IntPtr.Zero) { return false; }
                return WinAPI.Functions.SwitchDesktop(handle);

            }
            #endregion

            // ----------------------

            #region InitializeDesktop

            public static void InitializeDesktop(string name)
            {
                // If the desktop doesn't exist, create it.
                if (!VirtualDesktops.DesktopExists(name))
                {
                    VirtualDesktops.CreateDesktop(name);
                    Processes.CreateProcess(name, Environment.SpecialFolder.Windows.ToString() + @"\System32\explorer.exe", null);
                    Processes.CreateProcess(name, ".\\uShellDesktop.exe", null);
                }
            }

            #endregion
        }

        // Begin the window class.
        public class WindowManipulation
        {
            #region GetDesktopWindowsCaptions

            #region Prerequisites
            // Create an array to hold the window names.
            private static ArrayList mTitlesList;

            // Create a simple function to retrieve the names and add them
            // to the list.
            private static bool EnumWindowsProc(IntPtr hWnd, int lParam)
            {
                // Set a variable called title and fill its contents with the
                // current window's text.
                string title = GetWindowText(hWnd);

                // Add it to the list.
                mTitlesList.Add(title);

                // End the function and return "true".
                return true;
            }
            #endregion

            /// <summary>
            /// Returns the caption of all windows on the current desktop.
            /// </summary>
            /// <returns>Captions of all windows if (string) successful, otherwise nothing.</returns>
            public static string[] GetDesktopWindowsCaptions()
            {
                // Set the title list variable as a blank array.
                mTitlesList = new ArrayList();

                // Create an EnumDesktopWindowsDelegate called "enumfunc" for the
                // enumeration process.
                WinAPI.Functions.EnumDesktopWindowsDelegate enumfunc = new WinAPI.Functions.EnumDesktopWindowsDelegate(EnumWindowsProc);

                // Create an IntPtr called "hDesktop", which symbolizes
                // the current desktop.
                IntPtr hDesktop = IntPtr.Zero;

                // Create a boolean called success which will perform the specified
                // action and will later be checked for errors.
                bool success = WinAPI.Functions.EnumDesktopWindows(hDesktop, enumfunc, IntPtr.Zero);

                // If the enumeration is successful...
                if (success == true)
                {
                    // ... copy the results to string array...
                    string[] titles = new string[mTitlesList.Count];
                    mTitlesList.CopyTo(titles);

                    // ... and return the list of captions as an ArrayList.
                    return titles;
                }
                // If the enumeration fails...
                else
                {
                    // ...terminate the function and output nothing.
                    return null;
                }
            }
            #endregion

            #region GetWindowText
            /// <summary>
            /// Get the title of a window.
            /// </summary>
            /// <param hWnd="Window Handle">The handle of the specified window (hWnd).</param>
            /// <returns>The title of the window (string).</returns>
            public static string GetWindowText(IntPtr hWnd)
            {
                // Create a StringBuilder to store the title.
                StringBuilder title = new StringBuilder(WinAPI.Functions.MAX_TITLE);

                // Sets the window title length to an integer variable called
                // titleLength and then assigns its contents to the title variable.
                int titleLength = WinAPI.Functions.GetWindowText(hWnd, title, title.Capacity + 1);
                title.Length = titleLength;

                // Return the title of the window.
                return title.ToString();
            }
            #endregion

            #region ShowWindowByExec
            /// <summary>
            /// Set the window state of a window belonging to a specific executable program.
            /// </summary>
            /// <param exeName="Executable Name">The path to the executable whose window you want to control.</param>
            /// <param state="Window State">The action the window should make: "Hide", "Max", "Min", or "Restore".</param>
            /// <returns>True if successful, otherwise false.</returns>
            public static bool ShowWindowByExec(string exeName, string state)
            {
                // Set the window state codes.
                int SW_HIDE = 0;
                int SW_MAXIMIZE = 2;
                int SW_MINIMIZE = 1;
                int SW_RESTORE = 9;

                // Locate the process of the desired window and get its handle.
                Process[] proc = Process.GetProcessesByName(exeName);
                IntPtr hWnd = (IntPtr)proc[0].MainWindowHandle;

                // If the desired state is "Hide", then...
                if (state == "Hide")
                {
                    // ... hide the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_HIDE);
                }
                // If the desired state is "Max", then...
                else if (state == "Max")
                {
                    // ... maximize the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_MAXIMIZE);
                }
                // If the desired state is "Min", then...
                else if (state == "Min")
                {
                    // ... minimize the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_MINIMIZE);
                }
                // If the desired state is "Restore", then...
                else if (state == "Restore")
                {
                    // ... restore the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_RESTORE);
                }
                // Otherwise, if it isn't any recognizable command...
                else
                {
                    // ... the function returns "false".
                    return false;
                }
            }
            #endregion

            #region ShowWindowByName
            /// <summary>
            /// Set the window state of a window with a specific name.
            /// </summary>
            /// <param winName="Window Title">The title of the window to hide.</param>
            /// <param state="Window State">The action the window should make: "Hide", "Max", "Min", or "Restore".</param>
            /// <returns>True if successful, otherwise false.</returns>
            public static bool ShowWindowByName(string winName, string state)
            {
                // Set the window state codes.
                int SW_HIDE = 0;
                int SW_MAXIMIZE = 2;
                int SW_MINIMIZE = 1;
                int SW_RESTORE = 9;

                // Locate the window with the title specified and get its handle.
                IntPtr hWnd = WinAPI.Functions.FindWindow(winName, null);

                // If the desired state is "Hide", then...
                if (state == "Hide")
                {
                    // ... hide the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_HIDE);
                }
                // If the desired state is "Max", then...
                else if (state == "Max")
                {
                    // ... maximize the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_MAXIMIZE);
                }
                // If the desired state is "Min", then...
                else if (state == "Min")
                {
                    // ... minimize the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_MINIMIZE);
                }
                // If the desired state is "Restore", then...
                else if (state == "Restore")
                {
                    // ... restore the window of the program.
                    return WinAPI.Functions.ShowWindow(hWnd, SW_RESTORE);
                }
                // Otherwise, if it isn't any recognizable command...
                else
                {
                    // ... the function returns "false".
                    return false;
                }
            }
            #endregion
        }
    }
}